﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.ComponentModel;
using System.Diagnostics;

namespace GoogleFinance
{
    /// <summary>
    /// Represents a stock quote
    /// </summary>
    [DebuggerDisplay("{Symbol} Company={Company}")]
    public class StockQuote : IXmlNodeParsable, INotifyPropertyChanged
    {
        private string _divisor;
        private string _symbol;
        private string _company;
        private string _exchange;
        private string _currency;
        private string _last;
        private string _high;
        private string _low;
        private string _volume;
        private string _change;
        private string _percentageChange;
        private string _open;
        private string _close;
        private string _marketCap;
        private string _tradeDateUtc;
        private string _tradeTimeUtc;
        private string _currentDateUtc;
        private string _currentTimeUtc;

        private const string WingdingsUp = "ã";
        private const string WingdingsDown = "ä";

        /// <summary>
        /// The stock's symbol
        /// </summary>
        public string Symbol
        {
            get { return _symbol; }
            set { if (_symbol != value) { _symbol = value; OnPropertyChanged("Symbol"); } }
        }
        /// <summary>
        /// The company name for this stock
        /// </summary>
        public string Company
        {
            get { return _company; }
            set { if (_company != value) { _company = value; OnPropertyChanged("Company"); } }
        }
        /// <summary>
        /// The exchange that this stock is quoted on
        /// </summary>
        public string Exchange
        {
            get { return _exchange; }
            set { if (_exchange != value) { _exchange = value; OnPropertyChanged("Exchange"); } }
        }
        /// <summary>
        /// The curreny that this stock trades in
        /// </summary>
        public string Currency
        {
            get { return _currency; }
            set { if (_currency != value) { _currency = value; OnPropertyChanged("Currency"); } }
        }
        /// <summary>
        /// The last price for this stock
        /// </summary>
        public string Last
        {
            get { return _last; }
            set { if (_last != value) { _last = value; OnPropertyChanged("Last"); } }
        }
        /// <summary>
        /// The highest price for this stock today
        /// </summary>
        public string High
        {
            get { return _high; }
            set { if (_high != value) { _high = value; OnPropertyChanged("High"); } }
        }
        /// <summary>
        /// The lowest price for this stock today
        /// </summary>
        public string Low
        {
            get { return _low; }
            set { if (_low != value) { _low = value; OnPropertyChanged("Low"); } }
        }
        /// <summary>
        /// The trading volumne for this stock today
        /// </summary>
        public string Volume
        {
            get { return _volume; }
            set { if (_volume != value) { _volume = value; OnPropertyChanged("Volume"); } }
        }
        /// <summary>
        /// The price change for this stock today
        /// </summary>
        public string Change
        {
            get { return _change; }
            set { if (_change != value) { _change = value; OnPropertyChanged("Change"); } }
        }
        /// <summary>
        /// The percentage price change for this stock today
        /// </summary>
        public string PercentageChange
        {
            get { return _percentageChange; }
            set { if (_percentageChange != value) { _percentageChange = value; OnPropertyChanged("PercentageChange"); } }
        }
        /// <summary>
        /// The opening price for this stock today
        /// </summary>
        public string Open
        {
            get { return _open; }
            set { if (_open != value) { _open = value; OnPropertyChanged("Open"); } }
        }
        /// <summary>
        /// The closing price for this stock today
        /// </summary>
        public string Close
        {
            get { return _close; }
            set { if (_close != value) { _close = value; OnPropertyChanged("Close"); } }
        }
        /// <summary>
        /// The Market capitalization for this stock 
        /// </summary>
        public string MarketCap
        {
            get { return _marketCap; }
            set { if (_marketCap != value) { _marketCap = value; OnPropertyChanged("MarketCap"); } }
        }
        /// <summary>
        /// The stock's divisor
        /// </summary>
        public string Divisor
        {
            get { return _divisor; }
            set { if (_divisor != value) { _divisor = value; OnPropertyChanged("Divisor"); } }
        }
        /// <summary>
        /// The stock's last trade date in UTC
        /// </summary>
        public string TradeDateUtc
        {
            get { return _tradeDateUtc; }
            set { if (_tradeDateUtc != value) { _tradeDateUtc = value; OnPropertyChanged("TradeDateUtc"); } }
        }
        /// <summary>
        /// The stock's last trade time in UTC
        /// </summary>
        public string TradeTimeUtc
        {
            get { return _tradeTimeUtc; }
            set { if (_tradeTimeUtc != value) { _tradeTimeUtc = value; OnPropertyChanged("TradeTimeUtc"); } }
        }
        /// <summary>
        /// The current date in UTC of the google finance service
        /// </summary>
        public string CurrentDateUtc
        {
            get { return _currentDateUtc; }
            set { if (_currentDateUtc != value) { _currentDateUtc = value; OnPropertyChanged("CurrentDateUtc"); } }
        }
        /// <summary>
        /// The current time in UTC of the google finance service
        /// </summary>
        public string CurrentTimeUtc
        {
            get { return _currentTimeUtc; }
            set { if (_currentTimeUtc != value) { _currentTimeUtc = value; OnPropertyChanged("CurrentTimeUtc"); } }
        }
        /// <summary>
        /// The colour of the item - red if negative, green if positive
        /// </summary>
        public string ChangeColour
        {
            get { return (ChangeFloat==0) ? "Black" : (ChangeFloat < 0) ? "Red" : "Green"; }
        }
        /// <summary>
        /// Direction of change indicator (up/down arrow)
        /// </summary>
        public string ChangeDirectionCharacter
        {
            get { return (ChangeFloat == 0) ? "" : (ChangeFloat < 0) ? WingdingsDown : WingdingsUp; }
        }
        /// <summary>
        /// The change in the stock price expressed as a float
        /// </summary>
        private float ChangeFloat
        {
            get
            {
                return float.Parse(Change);
            }
        }

        /// <summary>
        /// Parses finance stock information into this class
        /// </summary>
        /// <param name="nodeIn">A <see cref="XmlNode"/> collection of finance nodes to parse</param>
        public void Parse(XmlNode nodeIn)
        {
            foreach(XmlNode node in nodeIn)
            {
                switch (node.Name)
                {
                    case "divisor":
                        Divisor = node.Attributes["data"].Value;
                        break;
                    case "symbol":
                        Symbol = node.Attributes["data"].Value;
                        break;
                    case "company":
                        Company = node.Attributes["data"].Value;
                        break;
                    case "exchange":
                        Exchange = node.Attributes["data"].Value;
                        break;
                    case "currency":
                        Currency = node.Attributes["data"].Value;
                        break;
                    case "last":
                        Last = node.Attributes["data"].Value;
                        break;
                    case "high":
                        High = node.Attributes["data"].Value;
                        break;
                    case "low":
                        Low = node.Attributes["data"].Value;
                        break;
                    case "volume":
                        Volume = node.Attributes["data"].Value;
                        break;
                    case "change":
                        Change = node.Attributes["data"].Value;
                        break;
                    case "perc_change":
                        PercentageChange = node.Attributes["data"].Value;
                        break;
                    case "open":
                        Open = node.Attributes["data"].Value;
                        break;
                    case "y_close":
                        Close = node.Attributes["data"].Value;
                        break;
                    case "market_cap":
                        MarketCap = node.Attributes["data"].Value;
                        break;
                    case "trade_date_utc":
                        TradeDateUtc = node.Attributes["data"].Value;
                        break;
                    case "trade_time_utc":
                        TradeTimeUtc = node.Attributes["data"].Value;
                        break;
                    case "current_date_utc":
                        CurrentDateUtc = node.Attributes["data"].Value;
                        break;
                    case "current_time_utc":
                        CurrentTimeUtc = node.Attributes["data"].Value;
                        break;
                }
            }
        }

        /// <summary>
        /// Copies an existing stock quote object's properties
        /// </summary>
        /// <param name="source"></param>
        public void CopyProperties(StockQuote source)
        {
            this.Change = source.Change;
            this.Close = source.Close;
            this.Company = source.Company;
            this.Currency = source.Currency;
            this.CurrentDateUtc = source.CurrentDateUtc;
            this.CurrentTimeUtc = source.CurrentTimeUtc;
            this.Divisor = source.Divisor;
            this.Exchange = source.Exchange;
            this.High = source.High;
            this.Last = source.Last;
            this.Low = source.Low;
            this.MarketCap = source.MarketCap;
            this.Open = source.Open;
            this.PercentageChange = source.PercentageChange;
            this.Symbol = source.Symbol;
            this.TradeDateUtc = source.TradeDateUtc;
            this.TradeTimeUtc = source.TradeTimeUtc;
            this.Volume = source.Volume;
        }
    
        #region INotifyPropertyChanged Members

        /// <summary>
        /// Raised when a property on this object has a new value.
        /// </summary>
        public event PropertyChangedEventHandler PropertyChanged;

        /// <summary>
        /// Warns the developer if this object does not have
        /// a public property with the specified name. This 
        /// method does not exist in a Release build.
        /// </summary>
        [Conditional("DEBUG")]
        [DebuggerStepThrough]
        private void VerifyPropertyName(string propertyName)
        {
            // Verify that the property name matches a real,  
            // public, instance property on this object.
            if (TypeDescriptor.GetProperties(this)[propertyName] == null)
            {
                string msg = "Invalid property name: " + propertyName;

                if (this.ThrowOnInvalidPropertyName)
                    throw new Exception(msg);
                else
                    Debug.Fail(msg);
            }
        }

        /// <summary>
        /// Returns whether an exception is thrown, or if a Debug.Fail() is used
        /// when an invalid property name is passed to the VerifyPropertyName method.
        /// The default value is false, but subclasses used by unit tests might 
        /// override this property's getter to return true.
        /// </summary>
        protected virtual bool ThrowOnInvalidPropertyName { get; private set; }

        /// <summary>
        /// Raises this object's PropertyChanged event.
        /// </summary>
        /// <param name="propertyName">The property that has a new value.</param>
        protected virtual void OnPropertyChanged(string propertyName)
        {
            this.VerifyPropertyName(propertyName);

            PropertyChangedEventHandler handler = this.PropertyChanged;
            if (handler != null)
            {
                var e = new PropertyChangedEventArgs(propertyName);
                handler(this, e);
            }
        }

        #endregion // INotifyPropertyChanged Members
    }
}
